package cn.redragon.soa.common.function;


import cn.redragon.soa.common.service.RedisService;
import cn.redragon.soa.common.util.JsonUtil;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.util.StringUtils;

public class RedisFunction<T> {

    protected final RedisService redisService;
    private final Class<T> tClass;
    protected String cacheKey;
    protected Long ttl;
    protected boolean allowNullValue;

    public RedisFunction(Class<T> tClass, RedisService redisService) {
        this.tClass = tClass;
        this.redisService = redisService;
    }

    public static <T> RedisFunction<T> with(Class<T> tClass, RedisService redisService) {
        Objects.requireNonNull(redisService);
        return new RedisFunction<>(tClass, redisService);
    }

    public RedisFunction<T> cacheKey(String cacheKey) {
        this.cacheKey = cacheKey;
        return this;
    }

    public RedisFunction<T> ttl(Long ttl) {
        this.ttl = ttl;
        return this;
    }

    public RedisFunction<T> allowNullValue(boolean allowNullValue) {
        this.allowNullValue = allowNullValue;
        return this;
    }

    public T getOrElseReload(Supplier<T> supplier) {
        return get(supplier, s -> JsonUtil.jsonToObject(s, tClass));
    }

    public Map<String, List<T>> getOrElseReloadMapList(Supplier<Map<String, List<T>>> supplier) {
        return get(supplier, s -> JsonUtil.jsonToObjMapList(s, tClass));
    }

    public List<T> listOrElseReload(Supplier<List<T>> supplier) {
        return get(supplier, s -> JsonUtil.jsonToObjList(s, tClass));
    }

    public List<T> linkedListOrElseReload(Supplier<List<T>> supplier) {
        return get(supplier, s -> JsonUtil.jsonToLinkedList(s, tClass));
    }

    protected <R> R get(Supplier<R> supplier, Function<String, R> function) {
        String value = redisService.get(cacheKey);
        if (StringUtils.hasText(value)) {
            return function.apply(value);
        }
        R s = supplier.get();
        if (s == null && !allowNullValue) {
            return null;
        }
        if (s != null && !allowNullValue && Collection.class.isAssignableFrom(s.getClass()) && CollectionUtils.isEmpty((Collection<?>) s)) {
            return s;
        }

        if (ttl != null) {
            redisService.setEx(cacheKey, JsonUtil.objectToJsonStrNullable(s), ttl);
        } else {
            redisService.set(cacheKey, JsonUtil.objectToJsonStrNullable(s));
        }

        return s;
    }
}
